﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Collections;

namespace EntityMetadata
{
    public partial class EntityModel
    {
        private Dictionary<string, DataTypeDef> _dataTypeDefs = new Dictionary<string, DataTypeDef>();
        private Dictionary<string, EntityDef> _entityDefs = new Dictionary<string, EntityDef>();
        private List<PropertyDef> _propertyDefs = new List<PropertyDef>();

        public Dictionary<string, DataTypeDef> DataTypeDefs
        {
            get
            {
                return _dataTypeDefs;
            }
        }

        public Dictionary<string, EntityDef> EntityDefs
        {
            get
            {
                return _entityDefs;
            }
        }

        public List<PropertyDef> PropertyDefs
        {
            get
            {
                return _propertyDefs;
            }
        }

        public void LoadFromExcel(ExcelReader reader)
        {
            DataSet ds = new DataSet();
            _tables.ToList<string>().ForEach(t => reader.FillTable(ds, t));
            LoadFromDataSet(ds);
        }

        public void LoadFromDataSet(DataSet ds)
        {
            FillDataTypes(ds.Tables[DataTypeTable]);
            FillPropertyDefs(ds.Tables[PropertyTable]);
            FillEntityTypes(ds.Tables[EntityTable]);

            // wire up datatypes to properties
            PropertyDefs.ForEach(x => x.DataType = GetDataTypeDef(x.DataTypeName));

            // wire up properties to entities
            foreach (EntityDef ed in EntityDefs.Values)
            {
                ed.Properties = GetPropertiesForEntity(ed);
                ed.Properties.ForEach(pd => pd.Entity = ed);

            }
        }

        private DataTypeDef GetDataTypeDef(string name)
        {
            if (!DataTypeDefs.ContainsKey(name))
            {
                throw new Exception("DataType: " + name + " was not defined.");
            }
            return DataTypeDefs[name];
        }

        private void FillPropertyDefs(DataTable t)
        {
            PropertyDef.GetSelectAllQuery(t).ToList<PropertyDef>().ForEach(pd => PropertyDefs.Add(pd));
        }

        private void FillDataTypes(DataTable t)
        {
            var query = DataTypeDef.GetSelectAllQuery(t);
            foreach (var item in query)
            {
                DataTypeDefs.Add(item.TypeName, item);
            }
        }

        private void FillEntityTypes(DataTable t)
        {
            IEnumerable<EntityDef> query = EntityDef.GetSelectAllQuery (t);
            foreach (var item in query)
            {
                EntityDefs.Add(item.EntityName, item);
            }
        }

        private List<PropertyDef> GetPropertiesForEntity(EntityDef e)
        {
            List<PropertyDef> entityProperties = new List<PropertyDef>();
            foreach( PropertyDef p in PropertyDefs.Where(x=>x.EntityName == e.EntityName) )
            {
                entityProperties.Add(p);
            }
            return entityProperties;
        }

    }

    public partial class EntityDef: DefBase
    {
        public List<PropertyDef> Properties { get; set; }

        public PropertyDef GetProperty(string name)
        {
            return Properties.Where(p => p.PropertyName == name).First();
        }

        public PropertyDef PrimaryKey
        {
            get
            {
                try
                {
                    return Properties.Where(p => p.PrimaryKey == true).First();
                }
                catch
                {
                    return null;
                }
            }
        }

        public static IEnumerable<EntityDef> GetSelectAllQuery(DataTable t)
        {
            var query =
                from r in t.AsEnumerable()
                select CreateInstance(r);
            return query;
        }
    }

    public partial class PropertyDef : DefBase
    {
        public DataTypeDef DataType { get; set; }
        public PropertyDef ReferencedProperty { get; set; }
        public EntityDef Entity { get; set; }

        public static IEnumerable<PropertyDef> GetSelectAllQuery(DataTable t)
        {
            var query =
                from r in t.AsEnumerable()
                select CreateInstance(r);
            return query;
        }
    }

    public partial class DataTypeDef : DefBase
    {
        /// <summary>
        /// Data type size constant, for types that do not have size specified.
        /// </summary>
        public const string UnspecifiedSize = "";

        /// <summary>
        /// Data type size constant, for maximum size allowed by data type.
        /// </summary>
        public const string MaxSize = "MAX";

        /// <summary>
        /// Data type scale constant, for types that do not have scale specified.
        /// </summary>
        public const int UnspecifiedScale = -1;

        public static IEnumerable<DataTypeDef> GetSelectAllQuery(DataTable t)
        {
            var query =
                from r in t.AsEnumerable()
                select CreateInstance(r);
            return query;
        }
    }
}
